package cn.ambeius.backend.tm;

import cn.ambeius.backend.utils.Panic;
import cn.ambeius.backend.utils.Parser;
import cn.ambeius.common.Error;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TransactionManagerImpl implements TransactionManager {

    /*
    TM 通过维护 XID 文件来维护事务的状态，并提供接口供其他模块来查询某个事务的状态。

    XID 文件给每个事务分配了一个字节的空间，用来保存其状态。
    同时，在 XID 文件的头部，还保存了一个 8 字节的数字，记录了这个 XID 文件管理的事务的个数。
    于是，事务 xid 在文件中的状态就存储在 (xid-1)+8 字节处，xid-1 是因为 xid 0（Super XID） 的状态不需要记录。
     */

    // XID文件头长度
    static final int LEN_XID_HEADER_LENGTH = 8;

    // 每个事务的占用长度
    private static final int XID_FIELD_SIZE = 1;

    // 事务的三种状态
    // 事务进行中
    private static final byte FIELD_TRAN_ACTIVE   = 0;
    // 事务已经提交
	private static final byte FIELD_TRAN_COMMITTED = 1;
    // 事务已经取消
	private static final byte FIELD_TRAN_ABORTED  = 2;

    // 超级事务，永远为commited状态
    public static final long SUPER_XID = 0;

    // 尾缀
    static final String XID_SUFFIX = ".xid";
    
    private RandomAccessFile file;
    private FileChannel fc;
    private long xidCounter;
    private Lock counterLock;

    TransactionManagerImpl(RandomAccessFile raf, FileChannel fc) {
        this.file = raf;
        this.fc = fc;
        counterLock = new ReentrantLock();
        checkXIDCounter();
    }

    /**
     * 功能描述：检查XID文件是否合法
     *          读取XID_FILE_HEADER中的xidcounter，根据它计算文件的理论长度，对比实际长度
     *
     * @author HeJiaWei
     * @date 22/01/2024
     */
    private void checkXIDCounter() {
        long fileLen = 0;
        try {
            fileLen = file.length();
        } catch (IOException e1) {
            Panic.panic(Error.BadXIDFileException);
        }
        if(fileLen < LEN_XID_HEADER_LENGTH) {
            Panic.panic(Error.BadXIDFileException);
        }

        // 创建一个 8 字节大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            fc.position(0);
            fc.read(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        // 获取XID
        this.xidCounter = Parser.parseLong(buf.array());
        // 获取xid文件的尾位置
        long end = getXidPosition(this.xidCounter + 1);
        if(end != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }
    }

    /**
     * 功能描述：根据事务xid取得其在xid文件中对应的位置
     *
     * @author HeJiaWei
     * @date 22/01/2024
     */
    private long getXidPosition(long xid) {
        return LEN_XID_HEADER_LENGTH + (xid-1)*XID_FIELD_SIZE;
    }


    /**
     * 功能描述：更新xid事务的状态为status
     *
     * @author HeJiaWei
     * @date 22/01/2024
     */
    private void updateXID(long xid, byte status) {
        // 获取偏移量（文件尾位置）
        long offset = getXidPosition(xid);
        byte[] tmp = new byte[XID_FIELD_SIZE];
        tmp[0] = status;
        ByteBuffer buf = ByteBuffer.wrap(tmp);
        try {
            fc.position(offset);
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            //metadata 参数表示是否也要将文件的元数据（如文件的创建时间、最后修改时间等）刷新到磁盘上。
            //如果为 true，则元数据也会被刷新；如果为 false（默认值），则只刷新数据。
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /**
     * 功能描述：将XID加一，并更新XID Header
     *
     * @author HeJiaWei
     * @date 22/01/2024
     */
    private void incrXIDCounter() {
        xidCounter ++;
        ByteBuffer buf = ByteBuffer.wrap(Parser.long2Byte(xidCounter));
        try {
            fc.position(0);
            fc.write(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    public long begin() {
        counterLock.lock();
        try {
            // 当前XID数加一
            long xid = xidCounter + 1;
            // 更新XID事务状态 -> 进行中
            updateXID(xid, FIELD_TRAN_ACTIVE);
            // 当前的XID数永久加一
            incrXIDCounter();
            return xid;
        } finally {
            counterLock.unlock();
        }
    }

    // 提交XID事务
    public void commit(long xid) {
        updateXID(xid, FIELD_TRAN_COMMITTED);
    }

    // 回滚XID事务
    public void abort(long xid) {
        updateXID(xid, FIELD_TRAN_ABORTED);
    }

    // 检测XID事务是否处于status状态
    private boolean checkXID(long xid, byte status) {
        long offset = getXidPosition(xid);
        ByteBuffer buf = ByteBuffer.wrap(new byte[XID_FIELD_SIZE]);
        try {
            fc.position(offset);
            fc.read(buf);
        } catch (IOException e) {
            Panic.panic(e);
        }
        return buf.array()[0] == status;
    }

    public boolean isActive(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ACTIVE);
    }

    public boolean isCommitted(long xid) {
        if(xid == SUPER_XID) return true;
        return checkXID(xid, FIELD_TRAN_COMMITTED);
    }

    public boolean isAborted(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXID(xid, FIELD_TRAN_ABORTED);
    }

    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

}